#ifndef LIBCELL_POINTS_CLOUD_ALGORITHM_H_
#define LIBCELL_POINTS_CLOUD_ALGORITHM_H_

#ifndef SIGN
#define SIGN(x) ((x>0)?1:((x<0)?-1:0))
#endif

#include<Mesh/_Func_.h>
#include<tool/libcell_tools_algorithm.h>
#include<tool/libcell_tools_octree.h>
#include<tool/libcell_tools_kd_node.h>
#include<Math/LB_Math.h>
// #include<Mesh/libcell_iterator.h>
// #include<tool/libcell_kd_node_iterator.h>

#ifdef __cplusplus
extern "C" {
#endif
// adjacent要修改
//
typedef struct Points_Open_Set{
    //n :: set of vertices
    //adjacent:: bigcap of open sets
    Node* n,*adjacent;
    template_v* v;
    double * normal;
}Points_Open_Set;

static inline void points_open_set_init(Points_Open_Set* pos)
{
    pos->n=NULL;pos->v=NULL;pos->adjacent=NULL;
    pos->normal=NULL;
}

static inline void points_open_set_free(Points_Open_Set* pos)
{
    if(pos==NULL)
    {
        return ;
    }
    free_node(pos->adjacent);
    free_node(pos->n);
    if(pos->normal!=NULL)
    {
        free(pos->normal);
    }
    free(pos);
}
Node* compute_points_open_sets(Node* n,Int_RB_Tree* tree,double r,KD_Node* kdtree);

double*compute_pca_result_using_clapack(Node* n);

void points_cloud_compute_normal_using_clapack(Node* n,KD_Node*kdtree,int num);

double**compute_pca_result(Node* n);


void points_cloud_compute_normal(Node* n,KD_Node*kdtree,int num);
void points_cloud_compute_normalr(Node* n,KD_Node*kdtree,double r);

// 一种更自然的调整法向的方法，基于谣言（或者真相）可行度的传播
// 一般来说调整r会影响法向的调整方式，
// num和fac影响迭代速度
// num是迭代次数，fac是迭代效率
//但是这种方法只适用大部分点的法向正确，只有局部错误的情况
void adjust_normal_of_node_verticesn(Node*n,double r,
    KD_Node* kdtree,int num,double fac);

void adjust_normal_of_node_vertices(Node*n,double r,KD_Node* kdtree);



// 可以考虑加上密度限制
//
/*
* 判断点云中的点是否是边界点
* @param tree: 点云的八叉树
* @param v: 点云中的点
* @param r: 会根据r搜索邻域点，并据此判断是否是边界
*/
int is_boundary_vertex_in_points_cloudr(
    KD_Node*kdtree,template_v* v,double r);
/*
// kdtree是整个点云的八叉树,主要用来判断方向
// kdtree1是boundaries的八叉树
// 需要v->prop储存法向信息
// fac: 允许膨胀系数,如果fac<=1.0,那么不允许膨胀，也就是loop每个线段的距离小于r,
// 否则,loop每个线段的距离一般来说小于r,但是严格来说是小于fac*r
//
// *****
// 返回的顺序是v在最后一个
*/
Node* get_boundary_loop_from_one_vertex(KD_Node* kdtree,
    double r,Node* boundaries, KD_Node* kdtree1,template_v* v,
    double fac);
//
// @kdtree1:: kd tree of boundaries
// @kdtree是整个网格的树

Node* connect_boundary_points(Mesh*mesh,KD_Node* kdtree,
    double r,Node* boundaries, Int_RB_Tree* tree);

Node* downsampling_through_oc_tree(Node* vertices,int depth);



// len是八叉树末端cube的边长
//  里面是八个线程
Node* downsampling_through_size(Node* vertices,double len );
Node* downsampling_through_size1(Node* vertices,double len );

//按距离聚类
Node* get_cluster_points_from_mesh1(Mesh * m,double r);


//求 data1 到data2的最优变换
double** compute_optimal_rigid_body_transformation(double** data1,double** data2,int len);

typedef struct ThreeD_Point_Pixel{
    Node* value;
    double center[3];

    void* prop;
}ThreeD_Point_Pixel;

static inline void threed_point_pixel_init(ThreeD_Point_Pixel*tdpp)
{
    tdpp->value=NULL;
    tdpp->center[0]=0;tdpp->center[1]=0;tdpp->center[2]=0;
    tdpp->prop=NULL;
}


ThreeD_Point_Pixel**** get_threed_points_pixels(
    Node* vertices,int fen);

void free_threed_points_pixels(ThreeD_Point_Pixel**** tdpp,int fen);

//
// 简单的补洞方法，vs是洞口的边界,vs也是容器内容会被修改。
//
Node* simple_fill_loop_hole_with_points(Mesh* m,
    template_v**vs,int len,double r);



//
// 用高斯核函数平滑点云位置
//
void gauss_smoothing_points_attris(Mesh* m,
    KD_Node* kdtree,double sigma,Int_RB_Tree*pos_tree,int size);

void gauss_smoothing_points_pos(Mesh* m,
    double sigma);


void average_smoothing_points_pos(Mesh* m,
    double r);



#ifdef __cplusplus
}
#endif

#endif
